package in.co.codedoc.sql;

import in.co.codedoc.cg.annotations.DBType;
import in.co.codedoc.ioc.IOCContainer;
import in.co.codedoc.sql.mapping.DoubleDoubleInputColumnMap;
import in.co.codedoc.sql.mapping.IntIntInputColumnMap;
import in.co.codedoc.sql.mapping.LongBigintInputColumnMap;
import in.co.codedoc.sql.mapping.ShortSmallintInputColumnMap;
import in.co.codedoc.sql.mapping.StringCharInputColumnMap;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Set;

public class UpdateStatement
	extends SQLStatement
{
	public UpdateStatement()
	{
		where = new WhereClause(this);
		columns = new LinkedList<TableColumn>();
		columnMaps = new LinkedList<DBInputColumnMap>();
	}

	@Override
	public PrintWriter Generate(PrintWriter out) throws IOException
	{
		out.write("UPDATE ");
		
		Set<Table> tables = where.GetTables();
		tables.addAll(GatherTables(columns));
		
		if(tables.isEmpty())
		{
			throw new RuntimeException
			(
				"Update stament has no set statement and no where clause."
			);
		}
		
		if(tables.size() > 1)
		{
			throw new RuntimeException
			(
				"Update statements updating more than table is not supported yet." +
				" Contact the framework developer."
			);
		}
		
		out.write(tables.iterator().next().GetTableName());
		
		out.write(" SET ");
		boolean firstColumn = true;
		
		for(TableColumn tc:columns)
		{
			if(!firstColumn)
			{
				out.write(',');
			}
			
			out.write(tc.GetQualifiedName());
			out.write(" = ?");
			
			firstColumn = false;
		}
		
		if(!where.IsEmpty())
		{
			out.write(" WHERE ");
			where.Generate(out);
		}
		
		return out;
	}

	@Override
	public void GetParameterValues(LinkedList<DBInputColumnMap> values)
	{
		values.addAll(columnMaps);
		where.GetParameterValues(values);
	}

	public UpdateStatement Set(TableColumn tc,IDBase v)
	{
		return Set(tc,new LongBigintInputColumnMap(v.GetId()));
	}
	
	public UpdateStatement Set(TableColumn tc,String v)
	{
		return Set(tc,new StringCharInputColumnMap(v));
	}
	
	public UpdateStatement Set(TableColumn tc,short v)
	{
		return Set(tc,new ShortSmallintInputColumnMap(v));
	}
	
	public UpdateStatement Set(TableColumn tc,int v)
	{
		return Set(tc,new IntIntInputColumnMap(v));
	}
	
	public UpdateStatement Set(TableColumn tc,long v)
	{
		return Set(tc,new LongBigintInputColumnMap(v));
	}
	
	public UpdateStatement Set(TableColumn tc,double v)
	{
		return Set(tc,new DoubleDoubleInputColumnMap(v));
	}

	public UpdateStatement Set(TableColumn tc,Object v,DBType dbType)
	{
		return Set(tc,IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v, dbType));
	}
		
	public UpdateStatement Set(TableColumn tc,DBInputColumnMap columnMap)
	{
		columns.add(tc);
		columnMaps.add(columnMap);
		return this;
	}

	public UpdateStatement Bind(TableColumn tc,IDBase v)
	{
		return Bind(tc,new LongBigintInputColumnMap(v.GetId()));
	}

	public UpdateStatement Bind(TableColumn tc,String v)
	{
		return Bind(tc,new StringCharInputColumnMap(v));
	}
	
	public UpdateStatement Bind(TableColumn tc,short v)
	{
		return Bind(tc,new ShortSmallintInputColumnMap(v));
	}
	
	public UpdateStatement Bind(TableColumn tc,int v)
	{
		return Bind(tc,new IntIntInputColumnMap(v));
	}
	
	public UpdateStatement Bind(TableColumn tc,long v)
	{
		return Bind(tc,new LongBigintInputColumnMap(v));
	}
	
	public UpdateStatement Bind(TableColumn tc,double v)
	{
		return Bind(tc,new DoubleDoubleInputColumnMap(v));
	}

	public UpdateStatement Bind(TableColumn tc,Object v,DBType dbType)
	{
		return Bind(tc,IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v, dbType));
	}
	
	public UpdateStatement Bind(TableColumn tc,DBInputColumnMap columnMap)
	{
		int index = -1;
		for(int i = 0;(index == -1)&& (i < columns.size());i++)
		{
			if(columns.get(i).equals(tc))
			{
				index = i;
			}
		}
		
		if(index == -1)
		{
			throw new RuntimeException
			(
				"Failed to bind value for " + tc +
				". Column not found in the update statement."
			);
		}
		else
		{
			columnMaps.set(index, columnMap);
		}
		
		return this;
	}

	
	public WhereClause Where()
	{
		return where;
	}
	
	private WhereClause where;
	private LinkedList<DBInputColumnMap> columnMaps;
	private LinkedList<TableColumn> columns;
}
